package com.ruoyi.common.utils.file;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.ObjectMetadata;
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.common.utils.uuid.Seq;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;

import com.ruoyi.common.utils.StringUtils;

import com.ruoyi.common.utils.DateUtils;


import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;

/**
 * 文件上传工具类（支持本地存储和阿里云OSS）
 */
@Component
public class FileUploadUtils {

    /**
     * 默认大小 50M
     */
    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L;

    /**
     * 默认的文件名最大长度 100
     */
    public static final int DEFAULT_FILE_NAME_LENGTH = 100;

    /**
     * 默认上传的地址（本地存储根目录）
     */
    private static String defaultBaseDir;

    /**
     * 云存储类型（aliyun/local）
     */
    private static String storageType;

    // 阿里云OSS配置参数（从配置文件注入）
    private static String aliyunOssEndpoint;
    private static String aliyunOssAccessKeyId;
    private static String aliyunOssAccessKeySecret;
    private static String aliyunOssBucketName;
    private static String aliyunOssDomain;

    // 静态化配置参数（通过@PostConstruct初始化）
    @Value("${ruoyi.profile}")
    public void setDefaultBaseDir(String baseDir) {
        FileUploadUtils.defaultBaseDir = baseDir;
    }

    @Value("${spring.cloud.storage.type:local}")
    public void setStorageType(String type) {
        FileUploadUtils.storageType = type;
    }

    @Value("${aliyun.oss.endpoint}")
    public void setAliyunOssEndpoint(String endpoint) {
        FileUploadUtils.aliyunOssEndpoint = endpoint;
    }

    @Value("${aliyun.oss.accessKeyId}")
    public void setAliyunOssAccessKeyId(String accessKeyId) {
        FileUploadUtils.aliyunOssAccessKeyId = accessKeyId;
    }

    @Value("${aliyun.oss.accessKeySecret}")
    public void setAliyunOssAccessKeySecret(String accessKeySecret) {
        FileUploadUtils.aliyunOssAccessKeySecret = accessKeySecret;
    }

    @Value("${aliyun.oss.bucketName}")
    public void setAliyunOssBucketName(String bucketName) {
        FileUploadUtils.aliyunOssBucketName = bucketName;
    }

    @Value("${aliyun.oss.domain}")
    public void setAliyunOssDomain(String domain) {
        FileUploadUtils.aliyunOssDomain = domain;
    }


    /**
     * 以默认配置进行文件上传（自动判断存储类型）
     */
    public static final String upload(MultipartFile file) throws IOException {
        try {
            return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        } catch (Exception e) {
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 根据文件路径上传（自动判断存储类型）
     */
    public static final String upload(String baseDir, MultipartFile file) throws IOException {
        try {
            return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        } catch (Exception e) {
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 文件上传核心方法（自动切换本地/云存储）
     */
    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
            InvalidExtensionException {
        return upload(baseDir, file, allowedExtension, false);
    }

    /**
     * 带自定义命名的文件上传
     */
    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension, boolean useCustomNaming)
            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
            InvalidExtensionException {

        // 1. 校验文件
        assertAllowed(file, allowedExtension);

        // 2. 生成文件名
        String fileName = useCustomNaming ? uuidFilename(file) : extractFilename(file);

        // 3. 根据存储类型上传
        if ("aliyun".equals(storageType)) {
            // 阿里云OSS存储
            return uploadToAliyunOSS(file, fileName);
        } else {
            // 本地存储
            return uploadToLocal(baseDir, file, fileName);
        }
    }


    // ------------------------------ 阿里云OSS上传逻辑 ------------------------------
    /**
     * 上传文件到阿里云OSS
     */
    private static String uploadToAliyunOSS(MultipartFile file, String fileName) throws IOException {
        // 1. 初始化OSS客户端
        OSS ossClient = new OSSClientBuilder().build(
                aliyunOssEndpoint,
                aliyunOssAccessKeyId,
                aliyunOssAccessKeySecret
        );

        try {
            // 2. 设置文件元数据（如Content-Type）
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentType(file.getContentType());
            metadata.setContentLength(file.getSize());

            // 3. 上传文件（BucketName + 文件名）
            ossClient.putObject(
                    aliyunOssBucketName,
                    fileName,
                    new ByteArrayInputStream(file.getBytes()),
                    metadata
            );

            // 4. 生成访问URL（格式：domain/fileName）
           return aliyunOssDomain + "/" + fileName;

        } finally {
            // 5. 关闭客户端
            ossClient.shutdown();
        }
    }


    // ------------------------------ 本地存储上传逻辑 ------------------------------
    /**
     * 上传文件到本地
     */
    private static String uploadToLocal(String baseDir, MultipartFile file, String fileName) throws IOException {
        // 1. 获取本地存储绝对路径
        File desc = getAbsoluteFile(baseDir, fileName);

        // 2. 写入文件
        file.transferTo(Paths.get(desc.getAbsolutePath()));

        // 3. 生成访问URL（本地资源路径）
        return getPathFileName(baseDir, fileName);
    }

    /**
     * 获取本地存储的绝对文件
     */
    public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
        File desc = new File(uploadDir + File.separator + fileName);
        if (!desc.exists()) {
            if (!desc.getParentFile().exists()) {
                desc.getParentFile().mkdirs();
            }
        }
        return desc;
    }

    /**
     * 生成本地存储的访问路径
     */
    public static final String getPathFileName(String uploadDir, String fileName) throws IOException {
        int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
        return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
    }


    // ------------------------------ 通用工具方法 ------------------------------
    /**
     * 生成带日期的文件名（日期目录 + 原文件名 + 序列值 + 后缀）
     */
    public static final String extractFilename(MultipartFile file) {
        return StringUtils.format(
                "{}/{}_{}.{}",
                DateUtils.datePath(), // 日期路径（如2025/08/12）
                FilenameUtils.getBaseName(file.getOriginalFilename()), // 原文件前缀
                Seq.getId(Seq.uploadSeqType), // 序列号（防重名）
                getExtension(file) // 文件后缀
        );
    }

    /**
     * 生成UUID文件名（日期目录 + UUID + 后缀）
     */
    public static final String uuidFilename(MultipartFile file) {
        return StringUtils.format(
                "{}/{}.{}",
                DateUtils.datePath(),
                IdUtils.fastSimpleUUID(), // UUID
                getExtension(file)
        );
    }

    /**
     * 文件大小和类型校验
     */
    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, InvalidExtensionException {
        // 校验大小
        long size = file.getSize();
        if (size > DEFAULT_MAX_SIZE) {
            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
        }

        // 校验类型
        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, fileName);
            } else {
                throw new InvalidExtensionException(allowedExtension, extension, fileName);
            }
        }
    }

    /**
     * 判断文件后缀是否允许
     */
    public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
        for (String str : allowedExtension) {
            if (str.equalsIgnoreCase(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取文件后缀
     */
    public static final String getExtension(MultipartFile file) {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StringUtils.isEmpty(extension)) {
            extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
        }
        return extension;
    }

    // ------------------------------ Getter ------------------------------
    public static String getDefaultBaseDir() {
        return defaultBaseDir;
    }

    public static String getStorageType() {
        return storageType;
    }
}